/*
@Time : 2022/9/14 11:07
@Author : gaozhichang
@File : goroutine_leak1.go
@des: 携程泄露
*/
package main

import (
	"fmt"
	"math/rand"
	"os"
	"runtime"
	"time"
)

func main() {
	//产生一个不停写数据的chan
	newRandStream := func() <-chan int {
		randStream := make(chan int)
		go func() {
			defer fmt.Println("newRandStream exit")
			defer close(randStream)
			for {
				fmt.Println("write to randStream")
				randStream <- rand.Intn(10)
			}
		}()

		return randStream
	}

	randStream := newRandStream()
	//fmt.Println("3 random stream:")
	//for i := 1; i <= 3; i++ {
	//	fmt.Printf("%d:%d\n", i, <-randStream)
	//}
	//
	//fmt.Fprintf(os.Stderr, "%d\n", runtime.NumGoroutine())
	//time.Sleep(10e9)
	//fmt.Fprintf(os.Stderr, "%d\n", runtime.NumGoroutine())
	//
	//fmt.Printf("%d:%d\n", 66, <-randStream)
	//time.Sleep(1 * time.Second)

	/**
	生产协程进入死循环，不断产生数据，消费协程，也就是主协程只消费其中的3个值，然后主协程就再也不消费channel中的数据，去做其他的事情了。此时生产协程放了一个数据到channel中，但已经不会有协程消费该数据了，所以，生产协程阻塞。
	此时，若没有人再消费channel中的数据，生成协程是被泄露的协程。

	解决办法
	总的来说，要解决channel引起的goroutine leak问题，主要是看在channel阻塞goroutine时，该goroutine的阻塞是正常的，还是可能导致协程永远没有机会执行。若可能导致协程永远没有机会执行，则可能会导致协程泄露。
	所以，在创建协程时就要考虑到它该如何终止。
	*/
	newRandStream2 := func(done <-chan interface{}) <-chan int {
		randStream := make(chan int)
		go func() {
			defer fmt.Println("newRandStream2 exit")
			defer close(randStream)
			for {
				select {
				case <-done:
					return
				case randStream <- rand.Intn(10):
					fmt.Println("write to randStream2")
				}
			}
		}()

		return randStream
	}

	done := make(chan interface{})
	randStream = newRandStream2(done)
	fmt.Println("3 random stream:")
	for i := 1; i <= 3; i++ {
		fmt.Printf("%d:%d\n", i, <-randStream)
	}
	close(done)

	fmt.Fprintf(os.Stderr, "%d\n", runtime.NumGoroutine())
	time.Sleep(10e9)
	fmt.Fprintf(os.Stderr, "%d\n", runtime.NumGoroutine())

	fmt.Printf("%d:%d\n", 66, <-randStream)
	time.Sleep(1 * time.Second)
}
